use std::{borrow::Cow, ffi::c_char};

use crate::{error::TonglunError, values::Complex32};

pub struct FFIUtils;

impl FFIUtils {
    pub fn c_strlen(s: *const c_char) -> usize {
        let mut len = 0;
        while unsafe { *s.add(len) } != 0 {
            len += 1;
        }
        len
    }

    pub fn c_str_to_string(s: *const c_char) -> Result<String, TonglunError> {
        let len = Self::c_strlen(s);
        let slice = unsafe { std::slice::from_raw_parts(s as *const u8, len) };

        let output = match String::from_utf8_lossy(slice) {
            Cow::Borrowed(s) => {
                let mut empty_str = String::new();
                if let Err(_) = empty_str.try_reserve(len) {
                    return Err(TonglunError::OutOfMemory);
                }

                empty_str.push_str(s);
                empty_str
            }
            // In this case, possible to oom and crash
            // but we can do nothing with Stable Rust...
            Cow::Owned(s) => s,
        };
        Ok(output)
    }
}

/// NULL-terminated string
/// 可以安全地获取只读的 C 字符串指针
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct NullStr(String);

impl NullStr {
    pub fn new(s: &str) -> Self {
        let s = s.to_string();
        s.into()
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }

    pub unsafe fn as_c_str(&self) -> *const c_char {
        self.0.as_ptr() as *const c_char
    }
}

impl From<String> for NullStr {
    fn from(mut s: String) -> Self {
        if !s.ends_with('\0') {
            s.push('\0');
        }
        Self(s)
    }
}

/// We prefer to use `std::mem::transmute` to support early versions of Rust
pub trait TonglunBitcast: Sized + Copy {
    type Unsigned;
    fn _tl_to_unsigned(self) -> Self::Unsigned;
    fn _tl_from_unsigned(value: Self::Unsigned) -> Self;
}

impl TonglunBitcast for f32 {
    type Unsigned = u32;
    fn _tl_to_unsigned(self) -> Self::Unsigned {
        #[allow(unnecessary_transmutes)]
        unsafe {
            std::mem::transmute(self)
        }
    }

    fn _tl_from_unsigned(value: Self::Unsigned) -> Self {
        #[allow(unnecessary_transmutes)]
        unsafe {
            std::mem::transmute(value)
        }
    }
}

impl TonglunBitcast for f64 {
    type Unsigned = u64;
    fn _tl_to_unsigned(self) -> Self::Unsigned {
        #[allow(unnecessary_transmutes)]
        unsafe {
            std::mem::transmute(self)
        }
    }

    fn _tl_from_unsigned(value: Self::Unsigned) -> Self {
        #[allow(unnecessary_transmutes)]
        unsafe {
            std::mem::transmute(value)
        }
    }
}

impl TonglunBitcast for i64 {
    type Unsigned = u64;
    fn _tl_to_unsigned(self) -> Self::Unsigned {
        self as Self::Unsigned
    }

    fn _tl_from_unsigned(value: Self::Unsigned) -> Self {
        value as Self
    }
}

impl TonglunBitcast for Complex32 {
    type Unsigned = u64;
    fn _tl_to_unsigned(self) -> Self::Unsigned {
        #[allow(unnecessary_transmutes)]
        unsafe {
            std::mem::transmute(self)
        }
    }

    fn _tl_from_unsigned(value: Self::Unsigned) -> Self {
        #[allow(unnecessary_transmutes)]
        unsafe {
            std::mem::transmute(value)
        }
    }
}
